<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport"
    content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
  <meta name="description" content="Custom post processing effect." />
  <meta name="cesium-sandcastle-labels" content="Showcases, Post Processing" />
  <title>Cesium Demo</title>
  <script type="text/javascript" src="../Sandcastle-header.js"></script>
  <script type="text/javascript" src="../../../Build/CesiumUnminified/Cesium.js" nomodule></script>
  <script type="module" src="../load-cesium-es6.js"></script>
  <script src="./brush.js"></script>
</head>

<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
  <style>
    @import url(../templates/bucket.css);
  </style>
  <div id="cesiumContainer" class="fullSize"></div>
  <div id="loadingOverlay">
    <h1>Loading...</h1>
  </div>
  <button onclick="addStage()" style="position: fixed;right: 50%;">开启</button>
  <div id="toolbar"></div>
  <script id="cesium_sandcastle_script">
    window.startup = function (Cesium) {
      "use strict";
      //Sandcastle_Begin
      const viewer = new Cesium.Viewer("cesiumContainer", {
        shouldAnimate: true,
      });

      const position = Cesium.Cartesian3.fromDegrees(
        -123.0744619,
        44.0503706
      );
      const tileset = new Cesium.Cesium3DTileset({
        url: "../../SampleData/Cesium3DTiles/Fcfh/tileset.json",
      });

      tileset.readyPromise
        .then(function (tileset) {
          viewer.scene.primitives.add(tileset);
          viewer.zoomTo(
            tileset,
            new Cesium.HeadingPitchRange(
              0.0,
              -0.5,
              tileset.boundingSphere.radius * 2.0
            )
          );
        })
        .catch(function (error) {
          console.log(error);
        });
      /*const url = "../../SampleData/models/AgBuildings/H1建筑结构.gltf";
      viewer.trackedEntity = viewer.entities.add({
        name: url,
        position: position,
        model: {
          uri: url,
        },
      });
*/
     const fragmentShaderSource = `
        uniform sampler2D colorTexture;
        uniform sampler2D noiseTexture;
        varying vec2 v_textureCoordinates;
        uniform vec4 highlight;
        uniform sampler2D depthTexture;
        uniform vec2 delta;

        int modInt(int base,int t){
          int b2=base-(base/t)*t;
          return b2;
        }
        int andInt(int a, int b) {
            int result = 0;
            int bit = 1;
            for (int i = 0; i < 32; i++) {
                if ((modInt(a ,2) == 1) && (modInt(b ,2) == 1)) {
                    result += bit;
                }
                a /= 2;
                b /= 2;
                bit *= 2;
            }
            return result;
        }

        int xorInt(int a, int b) {
            return (a + b) - 2 * andInt(a ,b);
        }
        int absInt(int t){
          if(t<0)return -1*t;
          return t;
        }
        vec2 grad(ivec2 z) {
            // replace this anything that returns a random vector"
            // 2D to 1D  (feel free to replace by some other)"
            int n = z.x + z.y * 11111;

            // Hugo Elias hash (feel free to replace by another one)"
            n = xorInt(n *8192 , n);
            n = (n * (n * n * 15731 + 789221) + 1376312589) /65535;

            // Perlin style vectors"
            n = absInt(n-(int(n/8))*8);
            vec2 gr = vec2(n -(n/2*2), n/2) * 2.0 - 1.0;
            return (n >= 6) ? vec2(0.0, gr.x) : (n >= 4) ? vec2(gr.x, 0.0) : gr;
        }
   
        float noise(in vec2 p) {
            ivec2 i = ivec2(floor(p));
            vec2 f = fract(p);

            vec2 u = f * f * (3.0 - 2.0 * f); // feel free to replace by a quintic smoothstep instead"
            
            vec2 v1=grad(i + ivec2(0, 0));
            vec2 v2=f - vec2(0.0, 0.0);
            float v3=dot(v1, v2);
            vec2 v4=grad(i + ivec2(1, 0));
            return mix(mix(v3, dot(v4, f - vec2(1.0, 0.0)), u.x), mix(dot(grad(i + ivec2(0, 1)), f - vec2(0.0, 1.0)), dot(grad(i + ivec2(1, 1)), f - vec2(1.0, 1.0)), u.x), u.y);
       }
    
    
        vec4 czm_windowToEyeCoordinates2(vec4 fragmentCoordinate)
        {
            // 窗口坐标重建NDC坐标
            float x = 2.0 * (fragmentCoordinate.x - czm_viewport.x) / czm_viewport.z - 1.0;
            float y = 2.0 * (fragmentCoordinate.y - czm_viewport.y) / czm_viewport.w - 1.0;
            float z = (fragmentCoordinate.z - czm_viewportTransformation[3][2]) / czm_viewportTransformation[2][2];
            vec4 q = vec4(x, y, z, 1.0);

            // NDC坐标转裁剪（投影）坐标
            q /= fragmentCoordinate.w;

            // 裁剪（投影）坐标转y（相机）坐标

            float top = 100.0;
            float bottom = czm_frustumPlanes.y;

            float left = czm_frustumPlanes.z;
            float right = 100.0;

            float near = czm_currentFrustum.x;
            float far = czm_currentFrustum.y;
            q.x = (q.x * (right - left) + left + right) * 0.5;
            q.y = (q.y * (top - bottom) + bottom + top) * 0.5;
            q.z = (q.z * (near - far) - near - far) * 0.5;
            q.w = 1.0;

          return q;
        }

        vec4 czm_windowToEyeCoordinates1(vec2 fragmentCoordinateXY, float depthOrLogDepth)
        {
            // See reverseLogDepth.glsl. This is separate to re-use the pow.

            float near = czm_currentFrustum.x;
            float far = czm_currentFrustum.y;
            float log2Depth = depthOrLogDepth * czm_log2FarDepthFromNearPlusOne;
            float depthFromNear = pow(2.0, log2Depth) - 1.0;
            float depthFromCamera = depthFromNear + near;
            vec4 windowCoord = vec4(fragmentCoordinateXY, far * (1.0 - near / depthFromCamera) / (far - near), 1.0);
            vec4 eyeCoordinate = czm_windowToEyeCoordinates2(windowCoord);
            eyeCoordinate.w = 1.0 / depthFromCamera; // Better precision
            return eyeCoordinate;
        }

        vec3 getNormal(vec2 uVu){
            // 获取当前深度
            float depth = czm_unpackDepth(texture2D(depthTexture, uVu));

            vec4 positionEye = czm_windowToEyeCoordinates1(uVu, depth);

            // 采样相邻像素
            vec2 rightCoord = uVu + vec2(delta.x / czm_viewport.z, 0.0);
            vec2 topCoord = uVu + vec2(0.0, delta.y / czm_viewport.w);

            vec2 rightCoord2 = gl_FragCoord.xy + vec2(2.0, 0.0);
            vec2 topCoord2 = gl_FragCoord.xy + vec2(0.0, 2.0);

            float depthRight = czm_unpackDepth(texture2D(depthTexture, rightCoord));
            float depthTop = czm_unpackDepth(texture2D(depthTexture, topCoord));

            vec4 positionRight =czm_windowToEyeCoordinates1(rightCoord2, depthRight);
            vec4 positionTop = czm_windowToEyeCoordinates1(topCoord2, depthTop);

             // 计算切线向量
            vec3 dx = positionRight.xyz - positionEye.xyz;
            vec3 dy = positionTop.xyz - positionEye.xyz;

            // 计算法线（视图空间）
            vec3 normal = normalize(cross(dx, dy));

            // 转换到世界空间
            vec3 normalWorld = (czm_inverseView * vec4(normal, 0.0)).xyz;
            normalWorld = normalize(normalWorld);

            // 编码到[0,1]范围
            normalWorld = normalWorld * 0.5 + 0.5;
            return normalWorld;
      }

      float valueAtPoint(sampler2D image, vec2 coord, vec2 texel, vec2 point) {
          vec3 luma = vec3(0.299, 0.587, 0.114);
          return dot(texture2D(image, coord + texel * point).xyz, luma);
      }
      float valueAtPointNormal(sampler2D image, vec2 coord, vec2 texel, vec2 point) {
          vec3 luma = vec3(0.299, 0.587, 0.114);
          vec3 normal=getNormal(coord + texel * point);
          return dot(normal.xyz, luma);
      }
    float diffuseValue(int x, int y, vec2 uResolution, vec2 vUv) {
        float cutoff = 100.0;
        float offset = 0.5 / cutoff;
        float noiseValue = clamp(texture2D(noiseTexture, vUv).r, 0.0, cutoff) / cutoff - offset;
        return valueAtPoint(colorTexture, vUv + noiseValue, vec2(1.0 / uResolution.x, 1.0 / uResolution.y), vec2(x, y)) * 0.6;
    }
    float normalValue(int x, int y, vec2 uResolution, vec2 vUv) {
        float cutoff = 150.0;
        float offset = 0.5 / cutoff;
        float noiseValue = clamp(texture2D(noiseTexture, vUv).r, 0.0, cutoff) / cutoff - offset;
        return valueAtPointNormal(colorTexture, vUv + noiseValue, vec2(1.0 / uResolution.x, 1.0 / uResolution.y), vec2(x, y)) * 0.3;
    }
    float getValue(int x, int y, vec2 uResolution, vec2 vUv, vec2 FragCoordXY) {
        float noiseValue = noise(FragCoordXY);
        
        noiseValue = noiseValue * 2.0 - 1.0;
        noiseValue *= 2.0;
        return diffuseValue(x, y, uResolution, vUv) + normalValue(x, y, uResolution, vUv) * noiseValue;
    }
    float combinedSobelValue(vec2 uResolution, vec2 vUv, vec2 FragCoordXY) {
        // kernel definition (in glsl matrices are filled in column-major order)"
        float Gx00 = -1.0; 
        float Gx01 = -2.0; 
        float Gx02 = -1.0; 
        float Gx10 = 0.0; 
        float Gx11 = 0.0; 
        float Gx12 = 0.0; 
        float Gx20 = 1.0; 
        float Gx21 = 2.0; 
        float Gx22 = 1.0; 
       
        float Gy00 =Gx00; 
        float Gy01 = Gx01; 
        float Gy02 = Gx02; 
        float Gy10 = Gx11; 
        float Gy11 =Gx12; 
        float Gy12 = Gx12; 
        float Gy20 = Gx20; 
        float Gy21 = Gx21; 
        float Gy22 =Gx22; 

        // fetch the 3x3 neighbourhood of a fragment"

        float tx0y0 = getValue(-1, -1, uResolution, vUv, FragCoordXY);
        float tx0y1 = getValue(-1, 0, uResolution, vUv, FragCoordXY);
        float tx0y2 = getValue(-1, 1, uResolution, vUv, FragCoordXY);

        // second column"
        float tx1y0 = getValue(0, -1, uResolution, vUv, FragCoordXY);
        float tx1y1 = getValue(0, 0, uResolution, vUv, FragCoordXY);
        float tx1y2 = getValue(0, 1, uResolution, vUv, FragCoordXY);

        // third column"
        float tx2y0 = getValue(1, -1, uResolution, vUv, FragCoordXY);
        float tx2y1 = getValue(1, 0, uResolution, vUv, FragCoordXY);
        float tx2y2 = getValue(1, 1, uResolution, vUv, FragCoordXY);

        // gradient value in x direction"
        float valueGx = Gx00 * tx0y0 + Gx01 * tx1y0 + Gx02 * tx2y0 +
        Gx10 * tx0y1 + Gx11 * tx1y1 + Gx12 * tx2y1 +
        Gx20 * tx0y2 + Gx21 * tx1y2 + Gx22 * tx2y2;
        
        // gradient value in y direction"
        float valueGy = Gy00 * tx0y0 + Gy01 * tx1y0 + Gy02 * tx2y0 +
        Gy10 * tx0y1 + Gy11 * tx1y1 + Gy12 * tx2y1 +
        Gy20 * tx0y2 + Gy21 * tx1y2 + Gy22 * tx2y2;

        // magnitude of the total gradient"
        float G = (valueGx * valueGx) + (valueGy * valueGy);
        return clamp(G, 0.0, 1.0);
    }
        
       
    
    void main() {
      vec4 color = texture2D(colorTexture, v_textureCoordinates);
      
      vec2 uv=v_textureCoordinates;
      vec2 iResolution=czm_viewport.zw;
      vec2 fragCoord=uv*czm_viewport.zw;

      float sobelValue = combinedSobelValue(iResolution.xy, uv, fragCoord);
      
      sobelValue = smoothstep(0.01, 0.03, sobelValue);
      vec4 lineColor = vec4(0.32, 0.12, 0.2, 1.0);
      if(sobelValue > 0.1) {
          gl_FragColor = vec4(lineColor.xyz+getNormal(uv),1.0);
      } else {
          gl_FragColor = vec4(vec3(1.0),1.0);
      }
  
    }

    `;
    window.addedStage=undefined;
      function addStage(){
        if(window.addedStage){
          viewer.scene.postProcessStages.remove(window.addedStage);
          window.addedStage=null;
          return;
        }
        window.addedStage = viewer.scene.postProcessStages.add(
          new Cesium.PostProcessStage({
            fragmentShader: fragmentShaderSource,
            uniforms: {
              delta: function () {
                return new Cesium.Cartesian2(1.0, 1.0);
              },
              noiseTexture: './cloud-noise.png', // Load the box.jpg texture,
              scale: 1
            }
          })
        );
      }
      
      window.addStage= addStage;
      //Sandcastle_End
      //Sandcastle_End
      Sandcastle.finishedLoading();
    };
    if (typeof Cesium !== "undefined") {
      window.startupCalled = true;
      window.startup(Cesium);
    }
  </script>
</body>

</html>